/**
 * 210824：升级修改【three.js r120-r131】
 */
let THREE=require('three');
import Collider from "./collider";
import CONST from '../collision-const';
import ConvexHull from "../broad-component/convex-hull";

const MAX_COUNT=1e6;
const FILTER_SIZE=1e-4;
const isChildOf=(ary,v,filter)=>{
    for(let i=0;i<ary.length/3;i++){
        let dx=ary[i*3]-v.x;
        let dy=ary[i*3+1]-v.y;
        let dz=ary[i*3+2]-v.z;
        let dsq=dx*dx+dy*dy+dz*dz;
        if(dsq<filter){
            return true;
        }
    }
    return false;
};

export default class MeshCollider extends Collider{
    constructor(values={}){
        super(values);
    }
    get colliderType(){
        return CONST.COLLIDER_TYPE_MESH;
    }
    get isConvex(){
        return this._isConvex;
    }
    set isConvex(bol){
        console.log('cannot change the value of [isConvex],only convex supported for now!');
        // this._isConvex=bol;
        /**
         * todo:凹壳的相关逻辑及算法实现
         */
    }
    get bounds(){
        return this._box.clone().applyMatrix4(this.matrixWorld);
    }
    get box(){
        return this._box.clone();
    }
    get sphere(){
        return this._sphere.clone();
    }
    get vertices(){
        return this._vertices;
    }
    get edges(){
        return this._edges;
    }

    setFromObject(object){
        this._object=object;
        this._tM0.copy(this._object.matrixWorld);
        // this._tM1.getInverse(this._object.matrixWorld);20210824
        this._tM1.copy(this._object.matrixWorld).invert();
        this._object.applyMatrix(this._tM1);
        this._convexHull.setFromObject(this._object);
        this._object.applyMatrix(this._tM0);
        this._getConvexInfo();
    }

    _createDebug() {
        if(this._debug)return;
        if(!this._object){
            console.warn('needs to setup convex data before drawing Debug!');
            return;
        }
        let g=new THREE.BufferGeometry();
        g.setAttribute('position',new THREE.Float32BufferAttribute(this._debugVertices,3));
        g.setAttribute('normal',new THREE.Float32BufferAttribute(this._debugNormals,3));
        let m=new THREE.MeshStandardMaterial({
            color:this._props.color,
            transparent:true,
            opacity:0.2
        });
        this._debug=new THREE.Mesh(g,m);
        this.add(this._debug);
    }
    _destroyDebug() {
        if (!this._debug) return;
        this._debug.material.dispose();
        this._debug.geometry.dispose();
        this._debug = null;
    }

    _initVariable() {
        super._initVariable();
        this._isConvex=true;
        this._convexHull=new ConvexHull();
        this._object=null;
        this._vertices=[];
        this._debugVertices=[];
        this._debugNormals=[];
        this._edges=[];
        this._tM0=new THREE.Matrix4();
        this._tM1=new THREE.Matrix4();
        this._sphere=new THREE.Sphere();
        this._box=new THREE.Box3();
        this._tV0=new THREE.Vector3();
    }
    _getConvexInfo(){
        this._vertices.length=0;
        this._edges.length=0;
        this._box.makeEmpty();
        let points=[];
        let count=0;
        for(let i=0;i<this._convexHull.faces.length;i++){
            let face=this._convexHull.faces[i];
            let edge=face.edge;
            do{
                if(count>MAX_COUNT){
                    console.warn('Too much data can severely impact performance!');
                    break;
                }
                count++;
                let point=edge.head().point;
                this._box.expandByPoint(point);
                points.push(point);
                this._debugVertices.push(point.x,point.y,point.z);
                this._debugNormals.push(face.normal.x,face.normal.y,face.normal.z);
                edge=edge.next;
            }while (edge!==face.edge);
        }
        let size=this._box.min.distanceToSquared(this._box.max);
        let filterSize=size*FILTER_SIZE;
        for(let i=0 ;i< points.length;i++){
            let p=points[i];
            if(!isChildOf(this._vertices,p,filterSize)) {
                this._vertices.push(p.x, p.y, p.z);
            }
        }

        this._box.getBoundingSphere(this._sphere);
    }
    _initRaycast() {
        this._raycast = (raycaster, intersects,target) => {
            if (!this._object) return;
            if (this._object.geometry) {
                let oInt=[];
                this._object.raycast(raycaster,oInt);
                for(let itn of oInt ){
                    itn.object=target?target:this;
                    intersects.push(itn);
                }
            }
            for (let child of this._object.children) {
                if (child.isMesh) {
                    let oInt=[];
                    this._object.raycast(raycaster,oInt);
                    for(let itn of oInt ){
                        itn.object=target?target:this;
                        intersects.push(itn);
                    }
                }
            }
        };
    }
}